﻿namespace MapleRock.P2PEmulator.RGR
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Drawing;
  using System.Data;
  using System.Linq;
  using System.Text;
  using System.Windows.Forms;
  using System.Windows.Forms.DataVisualization.Charting;

  internal  partial class GraphPage : BasicRgrPage
  {
    private List<DataPoint> min = null;

    private List<DataPoint> avg = null;

    private List<DataPoint> max = null;

    public GraphPage()
    {
      InitializeComponent();

      this.splineMTrackBar.Value = 2;
      this.splineRhoTrackBar.Value = -5;
    }

    public override void OnPageEnter()
    {
      base.OnPageEnter();

      var xAxisText = string.Format("{0} ({1})", this.XAxisName, this.XAxisAbbr);
      this.graphChart.ChartAreas[0].AxisX.Title = xAxisText;

      var yAxisText = string.Format("{0} ({1})", this.YAxisName, this.YAxisAbbr);
      this.graphChart.ChartAreas[0].AxisY.Title = yAxisText;

      var depText = string.Format("{0}({1})", this.YAxisAbbr, this.XAxisAbbr);
      this.graphChart.Series[0].Name = "мин. " + depText;
      this.graphChart.Series[1].Name = "сред. " + depText;
      this.graphChart.Series[2].Name = "макс. " + depText;
      this.graphChart.Series[3].Name = "мин. крив. " + depText;
      this.graphChart.Series[4].Name = "сред. крив. " + depText;
      this.graphChart.Series[5].Name = "макс. крив. " + depText;
    }

    public override string  RgrPageName
    {
	    get 
      { 
        return string.Format("Засисимость {0}({1})", this.YAxisAbbr, this.XAxisAbbr);
      }
    }

    protected string XAxisAbbr 
    {
      get 
      {
        Debug.Assert(
          !string.IsNullOrEmpty(RgrData.Instance.VariableParameterAbbr),
          "!string.IsNullOrEmpty(RgrData.Instance.VariableParameterAbbr)");
        return RgrData.Instance.VariableParameterAbbr; 
      }
    }

    protected string XAxisName
    {
      get 
      {
        Debug.Assert(
          !string.IsNullOrEmpty(RgrData.Instance.VariableParameterName),
          "!string.IsNullOrEmpty(RgrData.Instance.VariableParameterName)");
        return RgrData.Instance.VariableParameterName; 
      }
    }

    protected virtual string YAxisAbbr 
    {
      get { throw new NotSupportedException(); }
    }

    protected virtual string YAxisName 
    {
      get { throw new NotSupportedException(); } 
    }

    protected void FillChartSeriesData(List<DataPoint> min, List<DataPoint> avg, List<DataPoint> max)
    {
      this.min = min;
      this.avg = avg;
      this.max = max;

      this.ResetPoints();

      this.ResetSpline();

      this.EvaluatePearsonCriteria();
    }

    private DataPoint[] Approximate(List<DataPoint> points)
    {
      var refX = new double[points.Count];
      var refY = new double[points.Count];

      for (var pointIndex = 0; pointIndex < points.Count; ++pointIndex)
      {
        Debug.Assert(points[pointIndex].XValue != 0, "points[pointIndex].XValue != 0");

        refX[pointIndex] = points[pointIndex].XValue;
        refY[pointIndex] = points[pointIndex].YValues[0];
      }

      alglib.spline1dinterpolant s = null;
      alglib.spline1dfitreport rep = null;
      int info = 0;
      int m = Math.Max(points.Count, this.splineMTrackBar.Value);
      double rho = this.splineRhoTrackBar.Value;
      alglib.spline1dfitpenalized(refX, refY, m, rho, out info, out s, out rep);
      Debug.Assert(info > 0, "info > 0");

      var dataPointsCount = 100;
      var result = new DataPoint[dataPointsCount];
      var delta = (refX.Max() - refX.Min()) / (double)dataPointsCount;
      for (var i = 0; i < dataPointsCount; ++i)
      {
        var t = refX.Min() + (i * delta);
        result[i] = new DataPoint(t, alglib.spline1dcalc(s, t));
      }

      return result;
    }

    private void EvaluatePearsonCriteria()
    {
      var x = this.max.Select(e => e.XValue).ToArray();
      var y = this.max.Select(e => e.YValues[0]).ToArray();

      var pearsonCriteria = alglib.pearsoncorr2(x, y);
      this.pearsonCriteriaLabel.Text = string.Format("Rxy = {0:F02}", pearsonCriteria);
    }

    private void SaveButton_Click(object sender, EventArgs e)
    {
      var saveFileDialog = new SaveFileDialog();

      saveFileDialog.Filter = "PNG Image (*.png)|*.png";
      saveFileDialog.RestoreDirectory = true;

      if (DialogResult.OK != saveFileDialog.ShowDialog())
      {
        return;
      }

      this.graphChart.SaveImage(saveFileDialog.FileName, ChartImageFormat.Png);
    }

    private void DataCountsCheckBox_CheckedChanged(object sender, EventArgs e)
    {
      foreach (var serie in this.graphChart.Series)
      {
        if (serie.ChartType == SeriesChartType.Point)
        {
          serie.Enabled = this.dataCountsCheckBox.Checked;
        }
      }
    }

    private void CurveCheckBox_CheckedChanged(object sender, EventArgs e)
    {
      foreach (var serie in this.graphChart.Series)
      {
        if (serie.ChartType == SeriesChartType.Line)
        {
          serie.Enabled = this.curveCheckBox.Checked;
        }
      }
    }

    private void ColorCheckBox_CheckedChanged(object sender, EventArgs e)
    {
      this.graphChart.Palette =
        this.colorCheckBox.Checked ? ChartColorPalette.Bright : ChartColorPalette.Grayscale;
    }

    private void ResetPoints()
    {
      Debug.Assert(this.min.Count > 0, "this.min.Count > 0");
      Debug.Assert(this.avg.Count > 0, "this.avg.Count > 0");
      Debug.Assert(this.max.Count > 0, "this.max.Count > 0");

      this.graphChart.Series[0].Points.Clear();
      this.graphChart.Series[1].Points.Clear();
      this.graphChart.Series[2].Points.Clear();

      foreach (var entry in this.min)
      {
        this.graphChart.Series[0].Points.Add(entry);
      }

      foreach (var entry in this.avg)
      {
        this.graphChart.Series[1].Points.Add(entry);
      }

      foreach (var entry in this.max)
      {
        this.graphChart.Series[2].Points.Add(entry);
      }
    }

    private void ResetSpline()
    {
      Debug.Assert(this.min.Count > 0, "this.min.Count > 0");
      Debug.Assert(this.avg.Count > 0, "this.avg.Count > 0");
      Debug.Assert(this.max.Count > 0, "this.max.Count > 0");

      this.graphChart.Series[3].Points.Clear();
      this.graphChart.Series[4].Points.Clear();
      this.graphChart.Series[5].Points.Clear();

      var minApprox = this.Approximate(this.min);
      foreach (var point in minApprox)
      {
        this.graphChart.Series[3].Points.Add(point);
      }

      var avgApprox = this.Approximate(this.avg);
      foreach (var point in avgApprox)
      {
        this.graphChart.Series[4].Points.Add(point);
      }

      var maxApprox = this.Approximate(this.max);
      foreach (var point in maxApprox)
      {
        this.graphChart.Series[5].Points.Add(point);
      }
    }

    private void SplineMTrackBar_Scroll(object sender, EventArgs e)
    {
      this.ResetSpline();
    }

    private void SplineRhoTrackBar_Scroll(object sender, EventArgs e)
    {
      this.ResetSpline();
    }
  }
}
